跳至主內容

React Hooks(三):Redux-React-Hook

React Hooks(三):Redux-React-Hook
Gordon Lau 劉偉中
2019-03-27

2019-09-03 筆者按: 現時最常用之React-Redux程式庫已加入React Hooks功能,因此讀者可以詳讀 本篇後續文章React Hooks(四)理解React Hooks在Redux應用上之最新發展。

React Hooks在React16.8.0的版本正式成為React的正式功能。正如前兩篇所言,React Hooks簡化了寫複雜代碼的難度,亦令React的函數式部件(Functional Components)亦能使用stateprops,可是傳說中React Hooks將會取代Redux呢?卻一直都是只聞樓梯響。這篇文章就會介紹一個筆者認為頗有前景的組件,就是在Github中的facebookincubator中的redux-react-hook

redux-react-hook github 截圖

一直都錯重點

Redux最革命性的創見(Innovation),在於為前端開發定下一個結構,分為state(狀態)、action(動作)、reducer等幾個重要概念。

1. 用戶互動觸發事件(event),以事件的數據建立一個新的action

2. Reducer是個函數,舊state,加action,成為一個新的state。也就是new_state = reducer(old_state , action)

3. state經由react-redux連結每個React部件,隨state改變,React亦會更新界面。

以上三點,可以綜合為以下一圖。

redux流程圖

Source

最重要的是,其實步驟1及步驟2與React本身毫無關係,因此Redux不一定要與React同用,例如ngrx就是一個為了在Angular之中使用Redux。

所以一直有意見認為React Hooks可以取代Redux,其實是捉錯用神,React Hooks最能夠取代的,其實是react-redux,也就是React與Redux相連的部份。而Redux本身,只是為前端代碼提供結構。情況與React Context很相似,當React Context一推出時,亦有聲音認為Context API將可取代Redux,其實最新版本的Redux,正正是基於Context API所開發,只有將Redux的核心概念,包括action、reducer、state等概念引入React,才可以真正取代Redux。可是迄今為止,尚未見有任何類似的計劃,始終React一直以簡潔聞名,再加入Redux,就令初學者之學習曲線更為陡斜。

當React團隊宣佈React Hooks正式發佈時,Github馬上就湧現幾個方案,使用React Hooks嘗試解決Redux同React連接代碼寫法繁瑣的問題。 Google一下,就找到以下幾個方案:

1. Facebook Incubator 的 Redux React Hooks

2. 用戶 philipp-spiess 的 Use Substate

3. 用戶 martynaskadisa 的 React use Redux

4. 用戶 brn 的 rrh

在這幾個方案之中,暫時最有前景的就是第一位的Redux React Hooks,現已包括在Facebook incubator中,也就是成為正式官方方案的機會相當大。

React Redux VS Redux React Hooks

用過Redux的朋友,都知道使用Redux 時,除了使用Redux本身之外,還需要安裝一個名為react-redux的部件; 要使用Redux React Hooks,則需要安裝redux-react-hooks

下文將會分別以react-redux及react-redux-hooks建設一個簡單網站,有增量(increment)及減量(decrement)之功能,結構非常簡單,十分適合作為比較之用。

完整例子

筆者為了方便解釋及比較兩種方法之異同,特意撰寫了一個例子,開放在Tecky的Github之上,如有興趣,各位可以在以下網址可以詳閱代碼。

https://github.com/teckyio/tecky-redux-react-hooks

而完整例子亦已經部署到Github Pages之上,畫面如下:

按下increment,數字就會加1;按下decrement,數字就會減1,不斷按鈕,就可以加加減減。

代碼結構

這個簡單網站,要用React加上Redux,結構將如下圖:

加入Redux後project檔案結構圖

比起上集介紹React Hooks的例子,今次明顯多了很多檔案,因為要運用Redux,需要設置一些基礎檔案,正因如此,才有reducers.jsstore.js等檔案。

相同之處

大家大概可以發現,上面多出了兩個名字以WithoutHooks結尾的檔案,而對其他檔案,不論是使用react-redux還是react-redux-hooks,都是一模一樣的。這正正就是如上文所言「步驟1及步驟2與React本身毫無關係」,因此不會有任何分別。

store.js之內,只是很簡單運用createStore建立一個新的Redux Store,任何對狀態(state)的更動都必須經由reducer去改動。

import {createStore} from 'redux';
import reducer from './reducers';

export const store  = createStore(reducer);

reducers.js有甚麼呢?

const initialState = {
    counter: 0
}

export default function reducer(state = initialState,action){
    switch(action.type){
        case "INCREMENT":
            return {counter: state.counter+1}
        case "DECREMENT":
            return {counter: state.counter-1}
        default:
            return state;
    }
}

initialState 就是一個有counter是0的數值,reducer就是一個函數,支援的action有兩個, 分別是INCREMENTDECREMENT,兩個動作都十分直接,一個將counter+1,一個將counter-1 。 要留意的是,筆者在不同的case之中,都重新返回一個新的物件(object)作為新的狀態,這是使用Redux的基本原則,必須嚴格遵守。

不同之處:React-Redux

要使用React Redux,indexWithoutHooks.js需如下:

import * as React from 'react';
import {Provider} from 'react-redux';
import ReactDOM from "react-dom";
import {store} from './store';
import Counter from './CounterWithoutHooks.';


ReactDOM.render(
  <Provider store={store}>
      <Counter name="Sara" />
  </Provider>, 
  document.getElementById("root")
);

只要將component(也就是Counter)放在Provider之內,就可以在Counter裏面讀取Redux Store。 CounterWithoutHooks.js 則需如此

import * as React from 'react';
import "./styles.css";

import {connect} from 'react-redux';

export function Counter(props) {
  const { counter,increment,decrement } = props;
  return (
    <div>
      <h1>
        Hello, {props.name}
        {counter} times
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
}

const mapStateToProps = (state)=>({
  counter: state.counter
});

const mapDispatchToProps = (dispatch)=>({
  increment:()=>dispatch({type:"INCREMENT"}),
  decrement:()=>dispatch({type:"DECREMENT"}),
})

export default connect(mapStateToProps,mapDispatchToProps)(Counter);

對本身就已熟悉React的人,很容易就可以理解部件本身結構,唯一要增加的,則是下面兩個新值:mapStateToPropsmapDispatchToProps。 mapStateToProps將Redux Store內的counter,對照到CounterWithoutHooksprops之中, 而mapDispatchToProps則將兩個動作:INCREMENT、DECREMENT對照到props的函數,兩個都是以map開頭,正正是為了對照兩大重點:state(狀態)與dispatch(分配動作),狀態與分配動作是Redux兩個不可或缺的部份。 而更重要的是,需要用到特殊函數connect才能使我們的部件正常連接到Redux,讀取Redux Store裏面的狀態。

不同之處:Redux-React-Hooks

要用redux-react-hooks,在index.js有一些不同:

import * as React from 'react';
import {StoreContext} from 'redux-react-hook';
import ReactDOM from "react-dom";
import {store} from './store';
import Counter from './Counter';

ReactDOM.render(
  <StoreContext.Provider value={store}>
      <Counter name="Sara" />
  </StoreContext.Provider>, 
  document.getElementById("root")
);

基本上除了Provider一個component及其props需要更改外,其他皆與react-redux的例子無異。

最大的更動,在Counter.js就可以看到,由於redux-react-hooks提供了useMappedStateuseDispatch,連接Counter的代碼可以大大簡化

import * as React from 'react';
import "./styles.css";
import {useMappedState,useDispatch} from 'redux-react-hook';

export default function Counter(props) {

    const counter = useMappedState(state=> state.counter);
    
    const dispatch = useDispatch();
    return (
        <div>
            <h1>
                Hello, {props.name}
                {counter} times
            </h1>
            <div>
                <button onClick={()=>dispatch({type:"INCREMENT"})}>Increment</button>
                <button onClick={()=>dispatch({type:"DECREMENT"})}>Decrement</button>
            </div>
        </div>
    );
}

一個useMappedState,就扮演了mapStateToProps的角色,使用useDispatch,更可以直接於部件裏使用dispatch,無需任何特殊函數。 其中一個更明顯的好處,在於Counter的props沒有依賴任何Redux的功能,因此要寫單元測試(Unit testing)就更為簡單

結論

由上面兩段代碼可見,React Hooks確實簡化了連接React及Redux之間的代碼,而React Hooks本質較為接近函數式思維(functional thinking),也令要寫好單元測試更為簡單。當然,如上面所言,Redux-React-Hooks尚未成為正式專案,大家可以密切留意,看看是否會成為未來標準連接React及Redux的部件。

留言

閱讀更多

到底React Hooks 有何特別?

到底React Hooks 有何特別?

到底React Hooks 有何特別?
Gordon Lau 劉偉中
2018-11-27

新近推出的React 16.7包括一個很有趣的功能,名字叫做React Hooks。看到這個名字,很多人會下意識認為是在講componentDidMount, componentDidUpdate等方法。但其實這些方法的正名是 React Lifecycle Method, 推出React Hooks是為了方便開發者多用functional component,但仍然能夠使用state及 props等重要功能。


到底React Hooks有何特別(二)?淺談useEffect及useReducer

到底React Hooks有何特別(二)?淺談useEffect及useReducer

到底React Hooks有何特別(二)?淺談useEffect及useReducer
Gordon Lau 劉偉中
2018-11-29

於本篇文章的上集,我們討論了useState如何令Stateful React Component簡化良多,此篇主要討論的是如何使 用useEffect。useEffect可以簡化state,很多人都提到React Hooks有可能可以完全取代Redux作為 React State Management的標準,正因如此。


Web Technology為何征服世界?

Web Technology為何征服世界?

Web Technology為何征服世界?
Gordon Lau 劉偉中
2019-02-05

2007年,蘋果宣佈發佈第一代iPhone,標誌智能電話時代的開始;一年之後Android亦宣告面世,從此時起,智能電話的發展迅速,Mobile App成為軟件的代 名詞,筆者初初成為軟件工程師時,總有朋友詢問我是否正在開發Mobile App,縱使筆者的專業一直都是網頁及後端開發之上。而其時亦有不少預測,預測[網站將會被Mobile App完全取代](https://searchenginewatch.com/sew/opinion/2414336/the-final-hurdle-is-cleared-apps-will-replace-websites)。網站所用的HTML、CSS、JS等,亦將成為歷史,送入博物館之內。


React Hooks(四):全函數式React

React Hooks(四):全函數式React

React Hooks(四):全函數式React
Gordon Lau 劉偉中
2019-09-09

筆者在上年十一月React Hooks剛發佈時,就寫過關於React Hooks的應用,如何簡化開發React 應用時要寫的程式碼,之後又介紹了Redux-React-Hooks這個筆者認為有不錯前途的組件,雖然隨著React-Redux加入了React Hooks的應用,現在寫React + Redux應用,已無需再寫長長的mapStateToProps及mapDispatchToProps。


索取課程大綱
提交後, 請檢查你的電郵
hello@tecky.iot.me/tecky_hub+852 9725 6400
green_org
商界展關懷 2019-2022
英國頒證機構 TQUK 認可中心
aws_partner
薯片叔叔共創社 重塑教育挑戰大獎
B Corp™ 認證共益企業
無障礙網頁內容指引 (WCAG) 2.1 AA 級
香港無障礙網頁 金獎
© 2024 Tecky Academy Limited